package com.yanfan.energy.task;

import com.yanfan.energy.domain.OverallEnergy;
import com.yanfan.energy.mapper.OverallEnergyDao;
import com.yanfan.energy.util.DateUtils;
import com.yanfan.energy.util.DoubleOperation;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * @Author huan
 * @Description
 * @ClassName 2023年03月27日 13:31:00
 * @Version 1.0
 **/
@Component("energyTask")
public class EnergyTask {

    public static final String DATE_TYPE = "dateType";
    public static final String BEGIN = "begin";
    public static final String END = "end";
    public static final Integer HOUR = 1;


    @Resource
    private SqlSessionFactory sqlSessionFactory;

    @Autowired
    private OverallEnergyDao energyDao;

    /**
     * 获取前一天的数据
     *
     * @return
     */
    public static Map<String, Date> getPreviousHourDayRange() {
        Map<String, Date> respMap = new HashMap();
        LocalDateTime now = LocalDateTime.now();
        LocalDate prevHourDate = now.minusHours(1).toLocalDate(); // 上一个小时所在的日期
        LocalDate startDate = prevHourDate; // 全天的开始日期
        LocalDate endDate = startDate.plusDays(1);
        respMap.put(BEGIN, Date.from(startDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
        respMap.put(END, Date.from(endDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
        return respMap; // 返回起始日期和结束日期
    }
//
//    private Map<String, OverallEnergy> queryYeasterdayData(Integer dateType, Map reqMap) {
//        reqMap.put(BEGIN, DateUtils.getYesterdayTimestamp());
//        reqMap.put(END, DateUtils.getTodayTimestamp());
//        reqMap.put(DATE_TYPE, HOUR);
//        /**
//         * 昨天的数据
//         */
//        return energyDataListToMap(dateType, reqMap);
//    }
//
//

    /**
     * 返回一个小时前所在的date 全月的开始日期和结束日期
     * <p>
     * 比如 当前日期为 2008-05-25 13:25
     * <p>
     * 一个小时前的日期段为：  2008-05-25 12:00 - 2008-05-25 13:00
     * <p>
     * 一个小时所在的全天日期为  2008-05-01 00:00  -2008-06-01 00:00 ,
     * <p>
     * 当前日期为 2008-05-01 00:25
     * <p>
     * 一个小时前的日期段为：  2008-04-30 23:00 - 2008-05-01 00:00
     * <p>
     * 一个小时所在的全天日期为  2008-04-01 00:00 - 2008-05-01 00:00
     *
     * @return
     */
    public static Map<String, Date> getPreviousHourMonthRange() {
        Map<String, Date> respMap = new HashMap();
        LocalDateTime now = LocalDateTime.now();
        LocalDate firstDayOfMonth = now.toLocalDate().withDayOfMonth(1); // 当月第一天的日期
        LocalDate prevHourDate = now.minusHours(1).toLocalDate(); // 上一个小时所在的日期
        LocalDate startDate = firstDayOfMonth; // 一月的开始日期
        LocalDate endDate = startDate.plusMonths(1); // 下个月的开始日期，也就是本月的结束日期
        if (prevHourDate.isBefore(firstDayOfMonth)) { // 如果上一个小时所在的日期在本月之前
            startDate = firstDayOfMonth.minusMonths(1); // 则起始日期为上一个月的一号
            endDate = firstDayOfMonth; // 而结束日期为本月的一号
        }
        respMap.put(BEGIN, Date.from(startDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
        respMap.put(END, Date.from(endDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
        return respMap; // 返回起始日期和结束日期
    }

    /**
     * @return
     */

    // 返回当前时间的上一个小时所在的全年日期段
    public static Map<String, Date> getPreviousHourYearRange() {
        Map<String, Date> respMap = new HashMap();
        LocalDateTime now = LocalDateTime.now();
        LocalDate firstDayOfYear = now.toLocalDate().withDayOfYear(1); // 今年第一天的日期
        LocalDate prevHourDate = now.minusHours(1).toLocalDate(); // 上一个小时所在的日期
        LocalDate startDate = firstDayOfYear; // 一年的开始日期
        LocalDate endDate = startDate.plusYears(1); // 下一年的开始日期，也就是本年的结束日期
        if (prevHourDate.isBefore(firstDayOfYear)) { // 如果上一个小时所在的日期在今年之前
            startDate = firstDayOfYear.minusYears(1); // 则起始日期为去年的一月一号
            endDate = firstDayOfYear; // 而结束日期为今年的一月一号
        }
        respMap.put(BEGIN, Date.from(startDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
        respMap.put(END, Date.from(endDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
        return respMap;
    }

    /**
     * 数据insert
     *
     * @param
     * @throws Exception
     */
    private void cumulativeData(Map<String, OverallEnergy> yesterdayData, Map<String, OverallEnergy> cumulativeTarget) {
        for (String key : yesterdayData.keySet()) {// 遍历近一小时数据
            // month表已存在 ，累加
            if (cumulativeTarget.containsKey(key)) {
                OverallEnergy yesterdayDayOverallEnergy = yesterdayData.get(key); // 昨天的数据
                // 把昨天的数据合并到月份里
                cumulativeTarget
                        .get(key)
                        .setTotalDosage(DoubleOperation.addNonNullTrack(yesterdayDayOverallEnergy.getTotalDosage(), cumulativeTarget
                                .get(key)
                                .getTotalDosage()));
                cumulativeTarget
                        .get(key)
                        .setTotalConsumption(DoubleOperation.addNonNullTrack(yesterdayDayOverallEnergy.getTotalConsumption(), cumulativeTarget
                                .get(key)
                                .getTotalConsumption()));
            } else {
                //  没有的话，把新数据直接放到 yesterdayBelongsMonthData Map 集合中
                cumulativeTarget.put(key, yesterdayData.get(key));
            }
        }
    }

    /**
     * 把不同日期维度 日 、月 、年 数据 转化成
     *
     * @param dateType
     * @param reqMap
     * @return
     */
    private Map<String, OverallEnergy> energyDataListToMap(Integer dateType, Map reqMap) {
        List<OverallEnergy> energyConsumptions = energyDao.queryEnergyConsumption(reqMap);
        if (CollectionUtils.isEmpty(energyConsumptions)) {
            return null;
        }
        return energyDataHandle(dateType, energyConsumptions);
    }

    /**
     * 数据insert
     *
     * @param dateType
     * @throws Exception
     */
    public void energyConsumptionBatch(Integer dateType, String beginDate, String endDate) throws Exception {
        Map reqMap = new HashMap();
        reqMap.put(DATE_TYPE, dateType);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        reqMap.put(BEGIN, sdf.parse(beginDate));
        reqMap.put(END, sdf.parse(endDate));
        Map<String, OverallEnergy> updateMap = energyDataListToMap(dateType, reqMap);
        if (updateMap == null) return;
        mybatisBatchInsert(1000, dateType, updateMap.values().stream().collect(Collectors.toList()));
        // 日期类型为 1 ，往over_all 里面添加数据
        if (dateType.equals(1)) {
            mybatisBatchInsertAll(1000, dateType, updateMap.values().stream().collect(Collectors.toList()));
        }
    }

    /**
     * 数据insert
     *
     * @param dateType
     * @throws Exception
     */
    public void energyConsumptionClearJob(Integer dateType, Integer clearNum) {
        Map reqMap = new HashMap();
        reqMap.put(DATE_TYPE, dateType);
        if (dateType.equals(1)) {
            reqMap.put(END, DateUtils.getAppointDayTimestamp(clearNum));
        } else if (dateType.equals(2)) {
            reqMap.put(END, DateUtils.getAppointMonthTimestamp(clearNum));
        } else if (dateType.equals(3)) {
            reqMap.put(END, DateUtils.getAppointYearTimestamp(clearNum));
        }
        energyDao.insertBeforeClear(reqMap);
    }

    private Map<String, OverallEnergy> energyDataHandle(Integer dateType, List<OverallEnergy> energyList) {
        Map<String, OverallEnergy> updateMap = new HashMap<>();
        // 天 合并到月表
        // 时分秒 都为0
        // 根据 type_code type  device_id, useDate 的年月日  把  total_dosage   total_cost   、spikes_price、peaks_price 、parity_price、lowEbb_price 相加
        /**
         * created_date    时分秒 都为0
         */
        AtomicBoolean backupFlag = new AtomicBoolean(true);
        energyList.forEach(e -> {
            /**
             * 判断数据是否有问题， 有问题触发数据  total_consumption  total_dosage
             *
             * 这块是为了备份数据， 观察数据哪里有问题
             *  backupFlag.get() 的目的是一旦发现有问题 只执行一次
             */

//            if ((e.getTotalConsumption()>10  ) && dateType.equals(1) && backupFlag.get()) {
//                // 备份数据
//                MySQLDatabaseBackupUtils.backup251();
//                backupFlag.set(false);
//            }

            // 继续合并数据
            String mapKey = getMapKey(e, dateType);
            if (updateMap.containsKey(mapKey)) {
                e.setTotalDosage(DoubleOperation.addNonNullTrack(e.getTotalDosage(), updateMap
                        .get(mapKey)
                        .getTotalDosage()));
                e.setTotalConsumption(DoubleOperation.addNonNullTrack(e.getTotalConsumption(), updateMap
                        .get(mapKey)
                        .getTotalConsumption()));
            } else {
                Date date = DateUtils.dateTime("yyyy-MM-dd HH:mm:ss", getFormatDate(e.getUseDate(), dateType));
                e.setUseDate(date);
                e.setCreatedDate(date);
            }
            updateMap.put(mapKey, e);
        });
        return updateMap;
    }

    private long mybatisBatchInsert(int batch, Integer dateType, List<OverallEnergy> energyList) throws Exception {
        long beginTime = System.currentTimeMillis();
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        Exception eInfo = null;
        try {
            OverallEnergyDao insertMapper = session.getMapper(OverallEnergyDao.class);
            for (int index = 0; index < energyList.size(); index++) {
                insertMapper.insertOrUpdateBatch2(energyList.get(index), dateType);
                if ((index != 0 && index % batch == 0) || index == energyList.size() - 1) {
                    session.commit();
                    session.clearCache();
                }
            }
        } catch (Exception e) {
            session.rollback();
            eInfo = e;
        } finally {
            session.close();
            if (eInfo != null) {
                throw eInfo;
            }
        }
        return System.currentTimeMillis() - beginTime;
    }

    /**
     * 批量往overall_energy_full 存
     *
     * @param batch
     * @param dateType
     * @param energyList
     * @return
     * @throws Exception
     */
    private long mybatisBatchInsertAll(int batch, Integer dateType, List<OverallEnergy> energyList) throws Exception {
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        long beginTime = System.currentTimeMillis();
        Exception eInfo = null;
        try {
            OverallEnergyDao insertMapper = session.getMapper(OverallEnergyDao.class);
            for (int index = 0; index < energyList.size(); index++) {
                insertMapper.insertOrUpdateAllData(energyList.get(index), dateType);
                if ((index != 0 && index % batch == 0) || index == energyList.size() - 1) {
                    session.commit();
                    session.clearCache();
                }
            }
        } catch (Exception e) {
            session.rollback();
            eInfo = e;
        } finally {
            session.close();
            if (eInfo != null) {
                throw eInfo;
            }
        }

        return System.currentTimeMillis() - beginTime;
    }

    private String getFormatDate(Date date, Integer dateType) {
        if (dateType.equals(1)) {
            return DateFormatUtils.format(date, "yyyy-MM-dd 00:00:00");
        } else if (dateType.equals(2)) {
            return DateFormatUtils.format(date, "yyyy-MM-01 00:00:00");
        } else if (dateType.equals(3)) {
            return DateFormatUtils.format(date, "yyyy-01-01 00:00:00");
        }
        return "";
    }

    private String getMapKey(OverallEnergy e, Integer dateType) {
        return e.getWorkShopId() + "-" + e.getType() + "-" + e.getDeviceId() + "-" + getFormatDate(e.getCreatedDate(), dateType) + "-" + e.getPrice();
    }

    /**
     * 数据insert  数据同步到 day表、month表、year表  数据 ,（有一个小时的延迟）
     *
     * @param dateType 日期类型 ： 1 同步数据到day表  2 同步数据到month表  3 同步数据到year表
     * @throws Exception
     */
    public void energyConsumptionJobHour(Integer dateType) throws Exception {
        Map reqMap = new HashMap();
        List<OverallEnergy> saveCollect = new ArrayList<>();
        // 查前一个小时的实时数据，并格式话为 Map key-value 键值对的形式
        Map<String, OverallEnergy> lastHourRealTimeData = getLastHourRealTimeData(dateType, reqMap);
        if (CollectionUtils.isEmpty(lastHourRealTimeData)) { // 每小时同步一次，如果前一小时的数据为空 就不执行
            return;
        }

        Map<String, Date> dateMap = getPreviousHourDateByDateType(dateType);
        reqMap.put(BEGIN, dateMap.get(BEGIN));
        reqMap.put(END, dateMap.get(END));
        reqMap.put(DATE_TYPE, dateType + 1);
        Map<String, OverallEnergy> cumulativeTarget = energyDataListToMap(dateType, reqMap);
        if (CollectionUtils.isEmpty(cumulativeTarget)) {
            saveCollect = lastHourRealTimeData.values().stream().collect(Collectors.toList());
            mybatisBatchInsert(1000, dateType, saveCollect);
            return;
        }
        //  遍历库里面的数据，相同就累加 ,
        cumulativeData(lastHourRealTimeData, cumulativeTarget);
        saveCollect = cumulativeTarget.values().stream().collect(Collectors.toList());
        mybatisBatchInsert(1000, dateType, saveCollect);
    }

    /**
     * 数据insert  数据同步到 full表 数据 ,（有一个小时的延迟）
     *
     * @param dateType 日期类型 ： 1 同步数据到day表  2 同步数据到month表  3 同步数据到year表
     * @throws Exception
     */
    public void energyFullConsumptionJobHour(Integer dateType) throws Exception {

    }

    public void energyFullConsumptionJobHour1(Integer dateType) throws Exception {
        Map reqMap = new HashMap();
        List<OverallEnergy> saveCollect = new ArrayList<>();
        // 查前一个小时的实时数据，并格式话为 Map key-value 键值对的形式
        Map<String, OverallEnergy> lastHourRealTimeData = getLastHourRealTimeData(dateType, reqMap);
        if (CollectionUtils.isEmpty(lastHourRealTimeData)) { // 每小时同步一次，如果前一小时的数据为空 就不执行
            return;
        }

        Map<String, Date> dateMap = getPreviousHourDateByDateType(dateType);
        reqMap.put(BEGIN, dateMap.get(BEGIN));
        reqMap.put(END, dateMap.get(END));
        reqMap.put(DATE_TYPE, dateType + 1);

        // 用来存放 一小时前日期 所属的本天数据
        Map<String, OverallEnergy> cumulativeTarget = null;
        // 库里数据库
        List<OverallEnergy> energyConsumptions = energyDao.queryEnergyConsumptionFull(reqMap);
        if (!CollectionUtils.isEmpty(energyConsumptions)) {
            cumulativeTarget = energyDataHandle(dateType, energyConsumptions);
        }
        // 库里没有本天的数据库,直接新增入库
        if (CollectionUtils.isEmpty(cumulativeTarget)) {
            saveCollect = lastHourRealTimeData.values().stream().collect(Collectors.toList());
        } else { // 合并到现有的数据
            //  遍历库里面的数据，相同就累加 ,
            cumulativeData(lastHourRealTimeData, cumulativeTarget);
            saveCollect = cumulativeTarget.values().stream().collect(Collectors.toList());
        }
        mybatisBatchInsertAll(1000, dateType, saveCollect);

    }

    /**
     * 获取 最近一小时的实时数据
     * <p>
     * 当前时间 2023-05-25 10:15 获取时间  2023-05-25 09:00 ，2023-05-25 10:00  范围内的实时数据
     *
     * @param dateType
     * @param reqMap
     * @return
     */
    private Map<String, OverallEnergy> getLastHourRealTimeData(Integer dateType, Map reqMap) {
        Map<String, Date> beforeOneHourDate = getBeforeOneHourDate();
        reqMap.put(BEGIN, beforeOneHourDate.get(BEGIN));
        reqMap.put(END, beforeOneHourDate.get(END));
        reqMap.put(DATE_TYPE, HOUR);
        List<OverallEnergy> energyConsumptions = energyDao.queryEnergyConsumption(reqMap);
        if (CollectionUtils.isEmpty(energyConsumptions)) {
            return null;
        }
        return energyDataHandle(dateType, energyConsumptions);
    }

    /**
     * 获取近一小时的时间 日期
     * <p>
     * 比如：
     * 当前时间 2023-05-25 10:15 获取时间  2023-05-25 09:00 ，2023-05-25 10:00  ；
     * 当前时间 2023-05-25 00:15 获取时间  2023-05-24 23:00 ，2023-05-25 00:00
     * <p>
     * 当前时间：2008-03-01T00:15:30.358  获取时间  2008-02-29 23:00 ，2008-03-01 00:00
     *
     * @return
     */

    public Map<String, Date> getBeforeOneHourDate() {
        Map<String, Date> respMap = new HashMap();

        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();

        // 计算一小时前的时间
        LocalDateTime oneHourAgo = now.minusHours(1);

        // 获取一小时前的日期
        LocalDateTime oneHourAgoDate = oneHourAgo.withMinute(0).withSecond(0).withNano(0);

        LocalDateTime oneHourAgoEndDate = now.withMinute(0).withSecond(0).withNano(0);

        // 塞入数据
        respMap.put(BEGIN, Date.from(oneHourAgoDate.atZone(ZoneId.systemDefault()).toInstant()));
        respMap.put(END, Date.from(oneHourAgoEndDate.atZone(ZoneId.systemDefault()).toInstant()));

        // 返回数据
        return respMap;
    }

    /**
     * 根据dateType返回前一个小时的时间范围
     * 日 ，返回前一小时全天的数据  ,这里要返回 前一小时 所在日期的 凌晨和次日的凌晨 ：
     * 当前 2023-05-25 12:11 返回   2023-05-24 00:00:00   和  2023-05-25 00:00:00
     * <p>
     * 月：
     * 当前日期为 2008-05-25 13:25 返回 2008-05-01 00:00  -2008-06-01 00:00
     * <p>
     * 年：
     *
     * @param dateType
     * @return
     */
    public Map<String, Date> getPreviousHourDateByDateType(Integer dateType) {
        if (dateType.equals(1)) { // 日 ，返回前一小时全天的数据  ,这里要返回 前一小时 所在日期的 凌晨和次日的凌晨 ；
            // 塞入数据
            return getPreviousHourDayRange();
        } else if (dateType.equals(2)) { //
            return getPreviousHourMonthRange();
        } else {
            return getPreviousHourYearRange();
        }
    }


}
